package com.example.domain;

import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.persistence.criteria.Predicate;
import java.util.ArrayList;
import java.util.List;


@Service
@Transactional(rollbackFor = { Exception.class })
public class BookDatabaseQueryService {

    private final BookRepository bookRepository;

    public BookDatabaseQueryService(BookRepository bookRepository) {
        this.bookRepository = bookRepository;
    }

    @Deprecated
    public BookQueryCmdResult queryByIsbnCode(String isbnCode) {
        List<Book> list;

        list = bookRepository.findByIsbnCode(isbnCode);
        if (list.isEmpty()) {
            return new BookQueryCmdResult(4, "Not found");
        }
        long nBooksTotal;
        long nBooksInCurrPage;
        long nPages;
        nBooksTotal = nBooksInCurrPage = list.size();
        nPages = 1;
        return new BookQueryCmdResult(list, nBooksTotal, nBooksInCurrPage, nPages);
    }

    /**
     * 按查询ISBN或书名查询
     *
     * @param param 查询条件
     * @return 返回多条匹配结果
     */
    public BookQueryCmdResult queryByIsbnOrBookTitleContaining(BookQueryCmdParam param) {
        Pageable pageable;
        Page<Book> page;
        Book target;

        target = new Book();

        // 入参有效性检查
        target.setIsbnCode("");
        if (param.getIsbnCode() != null) {
            target.setIsbnCode(param.getIsbnCode());
        }
        target.setTitle("");
        if (param.getBookTitle() != null) {
            target.setTitle(param.getBookTitle());
        }
        target.setAuthor(param.getAuthor());
        target.setPublisher(param.getPublisher());
        target.setBriefIntroduction(param.getBriefIntroduction());

        // 自定义查询条件
        Specification<Book> spec = (root, query, builder) -> {
            List<Predicate> list = new ArrayList<>();
            if (!target.getIsbnCode().isEmpty()) {
                Predicate e = builder.equal(root.get("isbnCode").as(String.class), target.getIsbnCode());
                list.add(e);
            }
            if (!target.getTitle().isEmpty()) {
                String pattern = String.format("%%%s%%", target.getTitle());
                Predicate e = builder.like(root.get("title").as(String.class), pattern);
                list.add(e);
            }
            return builder.or(list.toArray(new Predicate[0]));
        };
        pageable = PageRequest.of(param.getPageIndex(), param.getPageSize());
        page= bookRepository.findAll(spec, pageable);

        // 检查并返回查询结果
        if (page.getContent().isEmpty()) {
            return new BookQueryCmdResult(4, "Not found");
        }
        return new BookQueryCmdResult(page.getContent(),
                page.getTotalElements(),
                page.getNumberOfElements(),
                page.getTotalPages());
    }

    /**
     * 查询所有
     *
     * @param pageIndex 起始页码的下标, 从0开始
     * @param numPerPage 每页条数
     * @return 查询结果列表
     */
    public BookQueryCmdResult queryBookList(int pageIndex, int numPerPage) {
        Pageable pageable;
        Page<Book> page;

        pageable = PageRequest.of(pageIndex, numPerPage);
        page = bookRepository.findAll(pageable);
        return new BookQueryCmdResult(page.getContent(), page.getTotalElements(), page.getNumberOfElements(), page.getTotalPages());
    }
    /**
     * 创建一本新书
     *
     * TODO: 创建之前应检查书籍 ISBN 编码是否重复
     */
    public BookCreationCmdResult createNewBook(BookCreationCmdParam param) {
        Book savedBook;

        savedBook = bookRepository.save(
                new Book(param.getIsbnCode(),
                        param.getBookTitle(),
                        param.getAuthor(),
                        param.getPublisher(),
                        param.getBriefIntroduction()
                )
        );

        return new BookCreationCmdResult(savedBook.getId());
    }

    public static class BookCreationCmdParam {
        private String isbnCode;
        private String bookTitle;
        private String author;
        private String publisher;
        private String briefIntroduction;

        public BookCreationCmdParam(String isbnCode, String bookTitle, String author, String publisher, String briefIntroduction) {
            this.isbnCode = isbnCode;
            this.bookTitle = bookTitle;
            this.author = author;
            this.publisher = publisher;
            this.briefIntroduction = briefIntroduction;
        }

        public String getIsbnCode() {
            return isbnCode;
        }

        public void setIsbnCode(String isbnCode) {
            this.isbnCode = isbnCode;
        }

        public String getBookTitle() {
            return bookTitle;
        }

        public void setBookTitle(String bookTitle) {
            this.bookTitle = bookTitle;
        }

        public String getAuthor() {
            return author;
        }

        public void setAuthor(String author) {
            this.author = author;
        }

        public String getPublisher() {
            return publisher;
        }

        public void setPublisher(String publisher) {
            this.publisher = publisher;
        }

        public String getBriefIntroduction() {
            return briefIntroduction;
        }

        public void setBriefIntroduction(String briefIntroduction) {
            this.briefIntroduction = briefIntroduction;
        }
    }

    public static class BookCreationCmdResult {
        private final String bookId;

        public BookCreationCmdResult(String bookId) {
            this.bookId = bookId;
        }

        public String getBookId() {
            return bookId;
        }
    }

    public static class BookQueryCmdResult extends CmdResult {
        private final List<Book> bookList;
        private final long numOfTotalBooks;
        private final long numOfBooksInCurrentPage;
        private final long numOfPages;

        public BookQueryCmdResult(int errCode, String msg) {
            super(errCode, msg);
            this.bookList = new ArrayList<>();
            this.numOfTotalBooks = 0;
            this.numOfBooksInCurrentPage = 0;
            this.numOfPages = 0;
        }

        public BookQueryCmdResult(List<Book> bookList, long numOfTotalBooks, long numOfBooksInCurrentPage, long numOfPages) {
            super(0, "OK");
            this.bookList = bookList;
            this.numOfTotalBooks = numOfTotalBooks;
            this.numOfBooksInCurrentPage = numOfBooksInCurrentPage;
            this.numOfPages = numOfPages;
        }

        public List<Book> getBookList() {
            return bookList;
        }

        public long getNumOfTotalBooks() {
            return numOfTotalBooks;
        }

        public long getNumOfBooksInCurrentPage() {
            return numOfBooksInCurrentPage;
        }

        public long getNumOfPages() {
            return numOfPages;
        }
    }

    public static class BookQueryCmdParam {
        private int pageIndex;
        private int pageSize;
        private String id;
        private String isbnCode;
        private String bookTitle;
        private String author;
        private String publisher;
        private String briefIntroduction;

        public BookQueryCmdParam(int pageIndex, int pageSize, String id, String isbnCode, String bookTitle, String author, String publisher, String briefIntroduction) {
            this.pageIndex = pageIndex;
            this.pageSize = pageSize;
            this.id = id;
            this.isbnCode = isbnCode;
            this.bookTitle = bookTitle;
            this.author = author;
            this.publisher = publisher;
            this.briefIntroduction = briefIntroduction;
        }

        public int getPageIndex() {
            return pageIndex;
        }

        public void setPageIndex(int pageIndex) {
            this.pageIndex = pageIndex;
        }

        public int getPageSize() {
            return pageSize;
        }

        public void setPageSize(int pageSize) {
            this.pageSize = pageSize;
        }

        public String getId() {
            return id;
        }

        public void setId(String id) {
            this.id = id;
        }

        public String getIsbnCode() {
            return isbnCode;
        }

        public void setIsbnCode(String isbnCode) {
            this.isbnCode = isbnCode;
        }

        public String getBookTitle() {
            return bookTitle;
        }

        public void setBookTitle(String bookTitle) {
            this.bookTitle = bookTitle;
        }

        public String getAuthor() {
            return author;
        }

        public void setAuthor(String author) {
            this.author = author;
        }

        public String getPublisher() {
            return publisher;
        }

        public void setPublisher(String publisher) {
            this.publisher = publisher;
        }

        public String getBriefIntroduction() {
            return briefIntroduction;
        }

        public void setBriefIntroduction(String briefIntroduction) {
            this.briefIntroduction = briefIntroduction;
        }
    }
}
